package com.weilele.mvvm.view

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.view.*
import androidx.core.view.NestedScrollingChildHelper
import androidx.core.view.ViewCompat
import androidx.core.view.children
import com.weilele.mvvm.R
import com.weilele.mvvm.utils.activity.hadScrollToBottom
import com.weilele.mvvm.utils.activity.hadScrollToEnd
import com.weilele.mvvm.utils.activity.hadScrollToStart
import com.weilele.mvvm.utils.activity.hadScrollToTop
import kotlin.math.abs

/**
 *  支持嵌套滚动的子view
 */
open class UnConsumedView : BaseNestedScrollingParentLayout {

    private val config by lazy { ViewConfiguration.get(context) }
    private val scaledPagingTouchSlop by lazy { config.scaledPagingTouchSlop }
    private val scaledMinimumFlingVelocity by lazy { config.scaledMinimumFlingVelocity }
    private val scaledMaximumFlingVelocity by lazy { config.scaledMaximumFlingVelocity.toFloat() }

    private var velocityTracker: VelocityTracker? = null
        get() {
            if (field == null) {
                field = VelocityTracker.obtain()
            }
            return field
        }

    private val helper by lazy {
        NestedScrollingChildHelper(this).apply {
            isNestedScrollingEnabled = true
        }
    }

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        checkAttributeSet(attrs)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
        checkAttributeSet(attrs)
    }

    private fun checkAttributeSet(attrs: AttributeSet?) {
        attrs ?: return
        context.obtainStyledAttributes(attrs, R.styleable.UnConsumedView).also {
            acceptChildNestedScroll =
                it.getBoolean(
                    R.styleable.UnConsumedView_acceptChildNestedScroll,
                    acceptChildNestedScroll
                )
        }.recycle()
    }

    private var downX = 0f
    private var downY = 0f
    private val locationInWindow = intArrayOf(0, 0)
    private val consumed = intArrayOf(0, 0)

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        if (!isNestedScrollingEnabled) {
            return super.onTouchEvent(event)
        }
        velocityTracker?.addMovement(event)
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {
                downX = event.rawX
                downY = event.rawY
                helper.startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
                return true
            }
            MotionEvent.ACTION_MOVE -> {
                if (downX == 0f && downY == 0f) {
                    downX = event.rawX
                    downY = event.rawY
                    helper.startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
                } else {
                    val offX = downX - event.rawX
                    val offY = downY - event.rawY
                    val offXInt = offX.toInt()
                    val offYInt = offY.toInt()
                    onNeedDispatchNestedPreScroll(
                        offXInt,
                        offYInt,
                        consumed,
                        locationInWindow,
                    )
                    helper.dispatchNestedPreScroll(
                        offXInt,
                        offYInt,
                        consumed,
                        locationInWindow,
                    )
                    //获取剩余值
                    val leftX = offXInt - consumed[0]
                    val leftY = offYInt - consumed[1]
                    val consumedX = getConsumedX(leftX)
                    val consumedY = getConsumedY(leftY)
                    helper.dispatchNestedScroll(
                        consumedX,
                        consumedY,
                        leftX - consumedX,
                        leftY - consumedY,
                        locationInWindow
                    )
                    downX = event.rawX + offX - offXInt.toFloat()
                    downY = event.rawY + offY - offYInt.toFloat()
                }
            }
            MotionEvent.ACTION_UP,
            MotionEvent.ACTION_CANCEL -> {
                if (downX == 0f && downY == 0f) {
                    return super.onTouchEvent(event)
                }
                velocityTracker?.let {
                    velocityTracker = null
                    it.computeCurrentVelocity(1000, scaledMaximumFlingVelocity)
                    if (abs(it.yVelocity) > scaledMinimumFlingVelocity) {
                        helper.dispatchNestedPreFling(
                            -it.xVelocity, -it.yVelocity
                        )
                    }
                    it.clear()
                    it.recycle()
                }
                downX = 0f
                downY = 0f
                helper.stopNestedScroll()
            }
        }
        return super.onTouchEvent(event)
    }

    //---------------------------------对子view是否需要拦截做判断----------------------------------------
    private var touchX = 0f
    private var touchY = 0f
    private var isHadTouchSlop = false

    private fun getTouchView(x: Int, y: Int): View? {
        val rect = Rect()
        children.forEach {
            rect.set(it.left, it.top, it.right, it.bottom)
            if (rect.contains(x, y)) {
                return it
            }
        }
        return null
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        if (!isNestedScrollingEnabled) {
            return super.onInterceptTouchEvent(ev)
        }
        when (ev?.action) {
            MotionEvent.ACTION_DOWN -> {
                isHadTouchSlop = false
                touchX = ev.rawX
                touchY = ev.rawY
            }
            MotionEvent.ACTION_MOVE -> {
                if (isHadTouchSlop) {
                    return true
                }
                val offX = touchX - ev.rawX
                val offY = touchY - ev.rawY
                val onlyChild = getTouchView(ev.x.toInt(), ev.y.toInt())
                if ((offX > scaledPagingTouchSlop && onlyChild?.hadScrollToEnd() == true)
                    || (offX < -scaledPagingTouchSlop && onlyChild?.hadScrollToStart() == true)
                    || (offY > scaledPagingTouchSlop && onlyChild?.hadScrollToBottom() == true)
                    || (offY < -scaledPagingTouchSlop && onlyChild?.hadScrollToTop() == true)
                ) {
                    //发生了滑动
                    isHadTouchSlop = true
                    return true
                }
            }
            MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
                if (isHadTouchSlop) {
                    isHadTouchSlop = false
                    return true
                }
            }
        }
        return super.onInterceptTouchEvent(ev)
    }

    /**
     * 是否将自己的嵌套滚动分发给父亲view
     */
    override fun isNestedScrollingEnabled(): Boolean {
        return super.isNestedScrollingEnabled()
    }

    /**
     * 是否接受子view的嵌套滚动
     */
    var acceptChildNestedScroll = false

    override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean {
        if (!acceptChildNestedScroll) {
            return false
        }
        return super.onStartNestedScroll(child, target, axes, type)
    }

    /**
     * 如果还有未消耗的值
     */
    override fun onNestedScroll(
        target: View/*滚动的子view*/,
        dxConsumed: Int/*子view已经消耗*/,
        dyConsumed: Int/*子view已经消耗*/,
        dxUnconsumed: Int/*子view未消耗*/,
        dyUnconsumed: Int/*子view未消耗*/,
        type: Int,
        consumed: IntArray
    ) {

    }

    /**
     * 事件分发前回调，可以做一些初始化操作
     */
    open fun onNeedDispatchNestedPreScroll(
        dx: Int, dy: Int, consumed: IntArray,
        offsetInWindow: IntArray
    ) {
        consumed[0] = 0
        consumed[1] = 0
    }

    /**
     * 返回需要的消耗的值
     * [leftX]剩余未消耗的值
     */
    open fun getConsumedX(leftX: Int) = 0

    /**
     * 返回需要的消耗的值
     * [leftY]剩余未消耗的值
     */
    open fun getConsumedY(leftY: Int) = 0


}